package cn.xd.core.ddd.db;

import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.xd.core.converter.Converter;
import cn.xd.core.ddd.diff.Diff;
import cn.xd.core.ddd.diff.EntityDiff;
import cn.xd.core.ddd.diff.ListDiff;
import cn.xd.core.ddd.entity.BasicEntity;
import cn.xd.core.ddd.entity.Entity;
import cn.xd.core.ddd.manager.AggregateManager;
import cn.xd.core.ddd.manager.ThreadLocalAggregateManager;
import cn.xd.core.ddd.value.IdValue;
import cn.xd.core.entity.BasicDBEntity;
import cn.xd.core.enums.DiffTypeEnum;
import cn.xd.core.response.ObjectResult;
import cn.xd.core.service.BasicMpService;
import cn.xd.core.utils.CoreUtils;
import cn.xd.core.utils.ValueUtils;
import lombok.AccessLevel;
import lombok.Getter;

import java.lang.reflect.ParameterizedType;

/**
 * @author : mirzaLin
 * @data : 2023/11/19
 * @description : Repository封装类
 **/
public abstract class DbRepositorySupport<T extends Entity<ID>, ID extends IdValue> implements Repository<T, ID> {

    @Getter
    private final Class<T> targetClass;


    /**
     * 让AggregateManager去维护Snapshot
     */
    @Getter(AccessLevel.PROTECTED)
    private final AggregateManager<T, ID> aggregateManager;

    protected DbRepositorySupport() {
        targetClass = (Class<T>) ((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0];
        this.aggregateManager = new ThreadLocalAggregateManager<>(targetClass);
    }

    /**
     * 这几个方法是继承的子类应该去实现的
     */
    protected abstract void onInsert(T aggregate);

    protected abstract T onSelect(ID id);

    protected abstract void onUpdate(T aggregate, EntityDiff diff);

    protected abstract void onDelete(T aggregate);

    /**
     * Attach的操作就是让Aggregate可以被追踪
     */
    @Override
    public void attach(T aggregate) {
        if (aggregate != null) {
            this.aggregateManager.attach(aggregate);
        }
    }

    /**
     * Detach的操作就是让Aggregate停止追踪
     */
    @Override
    public void detach(T aggregate) {
        this.aggregateManager.detach(aggregate);
    }

    /**
     * Detach的操作就是让Aggregate停止追踪
     */
    @Override
    public void detachById(ID aggregate) {
        this.aggregateManager.detachById(aggregate);
    }

    @Override
    public ObjectResult<T> find(ID id) {
        T aggregate = this.onSelect(id);
        if (aggregate != null) {
            // 这里的就是让查询出来的对象能够被追踪。
            // 如果自己实现了一个定制查询接口，要记得单独调用attach。
            this.attach(aggregate);
        } else {
            this.detachById(id);
        }
        return ObjectResult.of(aggregate);
    }

    @Override
    public void remove(T aggregate) {
        this.onDelete(aggregate);
        // 删除停止追踪
        this.detach(aggregate);
    }

    @Override
    public void save(T aggregate) {
        if (aggregate == null) {
            return;
        }
        // 如果如果手动从数据库中删掉数据，但是之前查询的快照还会存在，这个时候不会执行查询操作

        // 如果没有ID，直接插入 或者 有id 但是快照中没有查到也是进行插入
        if (ValueUtils.isNull(aggregate.getId())
                || aggregateManager.find(aggregate.getId()) == null) {
            this.onInsert(aggregate);
            this.attach(aggregate);
            return;
        }

        // 做Diff
        EntityDiff diff = aggregateManager.detectChanges(aggregate);
        if (diff.isEmpty()) {
            return;
        }

        // 调用UPDATE
        this.onUpdate(aggregate, diff);

        // 最终将DB带来的变化更新回AggregateManager
        aggregateManager.merge(aggregate);
    }

    @Override
    public void save(T aggregate, Boolean isModify) {

        if (!isModify) {
            this.onInsert(aggregate);
            this.attach(aggregate);
            return;
        }

        // 做Diff
        EntityDiff diff = aggregateManager.detectChanges(aggregate);
        if (diff.isEmpty()) {
            return;
        }

        // 调用UPDATE
        this.onUpdate(aggregate, diff);

        // 最终将DB带来的变化更新回AggregateManager
        aggregateManager.merge(aggregate);
    }

    protected <R extends BasicEntity, V extends BasicDBEntity> void updateList(Func1<T, ?> field, EntityDiff diff, Converter<R, V> converter) {
        Class<V> baseDBEntityClass = getBaseDBEntityClass(converter);
        var basicMpService = CoreUtils.getBasicMpService(baseDBEntityClass);
        Object diff1 = diff.getDiff(LambdaUtil.getFieldName(field));
        if (diff1 instanceof ListDiff listDiffs) {
            for (Diff diff2 : listDiffs.getList()) {
                if (DiffTypeEnum.Added.equals(diff2.getDiffType())) {
                    R newValue = (R) diff2.getNewValue();
                    basicMpService.saveResult(newValue, converter).failThrow();
                } else if (DiffTypeEnum.Modified.equals(diff2.getDiffType())) {
                    if (diff2 instanceof EntityDiff diff21) {
                        if (diff21.isSelfModified()) {
                            V bean = diff21.getBean(baseDBEntityClass);
                            basicMpService.updateResult(bean);
                        }
                    }
                } else if (DiffTypeEnum.Removed.equals(diff2.getDiffType())) {
                    R newValue = (R) diff2.getNewValue();
                    basicMpService.removeById(newValue.getId().getValue());
                }
            }
        }
    }

    /**
     * 聚合根对象中的实体对象更新操作
     *
     * @param field
     * @param diff
     * @param <R>
     * @param <V>
     */
    protected <R extends BasicEntity, V extends BasicDBEntity> void entityUpdatedAdded(Func1<T, ?> field
            , EntityDiff diff, Converter<R, V> converter) {
        Class<V> baseDBEntityClass = getBaseDBEntityClass(converter);
        BasicMpService basicMpService = CoreUtils.getBasicMpService(baseDBEntityClass);
        Object childrenDiffObject = diff.getDiff(LambdaUtil.getFieldName(field));
        if (childrenDiffObject instanceof EntityDiff childrenDiff) {
            if (childrenDiff.isSelfModified()) {
                V bean = childrenDiff.getBean(getBaseDBEntityClass(converter));
                basicMpService.updateResult(bean);
            }
            if (childrenDiff.isSelfAdded()) {
                R newValue = (R) childrenDiff.getNewValue();
                basicMpService.saveResult(newValue, converter).failThrow();
            }
        }
    }

    /**
     * 获取实体类
     *
     * @param converter
     * @param <R>
     * @param <V>
     * @return
     */
    private <R extends BasicEntity, V extends BasicDBEntity> Class<R> getIdentifiableClass(Converter<R, V> converter) {
        Class genericInterface = (Class) converter.getClass().getGenericInterfaces()[0];
        ParameterizedType parameterizedType = (ParameterizedType) genericInterface.getGenericInterfaces()[0];
        return (Class<R>) parameterizedType.getActualTypeArguments()[0];
    }

    /**
     * 获取do类
     *
     * @param converter
     * @param <R>
     * @param <V>
     * @return
     */
    private <R extends BasicEntity, V extends BasicDBEntity> Class<V> getBaseDBEntityClass(Converter<R, V> converter) {
        Class genericInterface = (Class) converter.getClass().getGenericInterfaces()[0];
        ParameterizedType parameterizedType = (ParameterizedType) genericInterface.getGenericInterfaces()[0];
        return (Class<V>) parameterizedType.getActualTypeArguments()[1];
    }
}
